package com.huxi.lang.object.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class MultiMap<K, V> implements Map<K, V> {

	private Map<K, List<V>> map;

	@Override
	public int size() {
		return map.size();
	}

	@Override
	public boolean isEmpty() {
		return map.isEmpty();
	}

	@Override
	public boolean containsKey(Object key) {
		return map.containsKey(key);
	}

	@Override
	public boolean containsValue(Object value) {
		for (List<V> item : map.values()) {
			if (item != null && item.contains(value)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public V get(Object key) {
		List<V> item = map.get(key);
		return any(item);
	}

	@Override
	public V put(K key, V value) {
		List<V> item = map.get(key);
		if (item == null) {
			item = new ArrayList<>();
			map.put(key, item);
		}
		V rs = any(item);
		item.add(value);
		return rs;
	}

	@Override
	public V remove(Object key) {
		List<V> item = map.remove(key);
		return any(item);
	}

	@Override
	public void putAll(Map<? extends K, ? extends V> m) {
		if (!(m == null || m.size() == 0)) {
			for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
				List<V> item = map.get(entry.getKey());
				if (item == null) {
					item = new ArrayList<>();
					map.put(entry.getKey(), item);
				}
				item.add(entry.getValue());
			}
		}
	}

	@Override
	public void clear() {
		map.clear();
	}

	@Override
	public Set<K> keySet() {
		return map.keySet();
	}

	@Override
	public Collection<V> values() {
		if (map.size() == 0) {
			return Collections.emptyList();
		}
		ArrayList<V> rs = new ArrayList<>(map.size());
		for (List<V> item : map.values()) {
			rs.add(any(item));
		}
		return rs;
	}

	@Override
	public Set<Entry<K, V>> entrySet() {
		if (map.size() == 0) {
			return Collections.emptySet();
		}
		HashSet<Entry<K, V>> set = new HashSet<>();
		for (Entry<K, List<V>> entry : map.entrySet()) {
			set.add(any(entry.getKey(), entry.getValue()));
		}
		return set;
	}

	public class MultiEntry implements Entry<K, V> {
		private final K key;
		private final int index;
		private List<V> item;

		public MultiEntry(K key, int index, List<V> item) {
			this.key = key;
			this.index = index;
			this.item = item;
		}

		@Override
		public int hashCode() {
			return Objects.hashCode(key);
		}

		@Override
		public boolean equals(Object o) {
			return this == o;
		}

		@Override
		public K getKey() {
			return key;
		}

		@Override
		public V getValue() {
			return item != null && index < item.size() ? item.get(index) : null;
		}

		@Override
		public V setValue(V value) {
			if (item == null) {
				item = new ArrayList<>();
				map.put(key, item);
			}
			V rs = getValue();
			item.set(index, value);
			return rs;
		}

		public List<V> asList() {
			return item != null ? Collections.unmodifiableList(item) : Collections.emptyList();
		}

		public int size() {
			return item != null ? item.size() : 0;
		}

		public boolean isEmpty() {
			return item != null ? item.isEmpty() : true;
		}

		public V get(int index) {
			int len = size();
			if (index < 0 || index > len) {
				throw new ArrayIndexOutOfBoundsException(index);
			}
			if (index == len) {
				return null;
			}
			return item.get(index);
		}

		public V set(int index, V value) {
			int len = size();
			if (index < 0 || index >= len) {
				throw new ArrayIndexOutOfBoundsException(index);
			}
			return item.set(index, value);
		}

		public boolean add(V value) {
			if (item == null) {
				item = new ArrayList<>();
				map.put(key, item);
			}
			return item.add(value);
		}

		public boolean add(int index, V value) {
			if (item == null) {
				item = new ArrayList<>();
				map.put(key, item);
			}
			item.add(index, value);
			return true;
		}

		public ListIterator<V> iterator() {
			return item != null ? item.listIterator() : null;
		}

		public ListIterator<V> reverseIterator() {
			return item != null ? item.listIterator(item.size()) : null;
		}
	}

	protected final V first(List<V> list) {
		return !(list == null || list.size() == 0) ? list.get(0) : null;
	}

	protected final V last(List<V> list) {
		return !(list == null || list.size() == 0) ? list.get(list.size() - 1) : null;
	}

	protected V any(List<V> list) {
		return !(list == null || list.size() == 0) ? list.get(0) : null;
	}

	protected MultiEntry any(K key, List<V> value) {
		return new MultiEntry(key, 0, value);
	}

}
